(define unify
   X Y -> (unify-help X Y [ ]))

(define unify-help 
    X X Mgu -> Mgu
   [X | Y] [W | Z] Mgu 
   -> (let NewMgu (unify-help (dereference X Mgu) (dereference W Mgu) Mgu)
             (if (= NewMgu fail) fail (unify-help Y Z NewMgu))) 
   X Y Mgu -> [[X Y] | Mgu]     where (and (variable? X) (not (occurs? X Y))) 
   Y X Mgu -> [[X Y] | Mgu]     where (and (variable? X) (not (occurs? X Y))) 
   _ _ _ -> fail) 

(define dereference
   [X | Y] Mgu -> [(dereference X Mgu) | (dereference Y Mgu)] 
   X Mgu -> (let Val (assoc X Mgu) 
                         (if (empty? Val) X (dereference (head (tail Val)) Mgu)))) 

(define occurs?
   X X -> true
   X [Y | Z] -> (or (occurs? X Y) (occurs? X Z))
   _ _ -> false) 

(define tiny-prolog.v1
    Goals Clauses -> (prolog-loop.v1 Goals Clauses Clauses [ ]))

(define prolog-loop.v1 
    [ ] _ _ _ -> true 
    [Goal | Goals] [Clause | _] AllClauses Bindings 
   <- (fail-if (/. X (= X false))
                  (let NewClause (standardise-apart Clause) 
                        Mgu (unify-help (dereference Goal Bindings) 
                                                   (head NewClause) 
                                                   Bindings) 
                        (if (= Mgu fail) false 
                            (prolog-loop.v1 (append (body NewClause) Goals) 
                                                       AllClauses AllClauses Mgu)))) 
    Goals [_ | Clauses] AllClauses Bindings 
  -> (prolog-loop.v1 Goals Clauses AllClauses Bindings) 
  _ _ _ _ -> false) 

(define standardise-apart
  Clause -> (st Clause Clause))

(define st 
  X StClause -> (subst (gensym "X") X StClause) where (variable? X) 
  [X | Y] StClause -> (st X (st Y StClause)) 
  _ StClause -> StClause)

(define body 
  [_ <= | Body] -> Body) 
	
(define tiny-prolog.v2
    Goals Clauses -> (prolog-loop.v2 (add-answer-literal Goals)
                                                            Clauses Clauses [ ]))

(define add-answer-literal
    Goals -> (append Goals [(variables-in Goals)]))

(define variables-in
   X -> [X]	where (variable? X)
   [X | Y] -> (union (variables-in X) (variables-in Y))
   _ -> [ ])

(define prolog-loop.v2 
    [Answer] _ _ Bindings -> (answer Answer Bindings)
    [Goal | Goals] [Clause | _] AllClauses Bindings 
   <- (fail-if (/. X (= X false))
       (let NewClause (standardise-apart Clause) 
                        Mgu (unify-help (dereference Goal Bindings) 
                                                   (head NewClause) 
                                                   Bindings) 
                        (if (= Mgu fail) false 
                            (prolog-loop.v2 (append (body NewClause) Goals) 
                                                  AllClauses 
                                                  AllClauses 
                                                  Mgu)))) 
     Goals [_ | Clauses] AllClauses Bindings 
  -> (prolog-loop.v2 Goals Clauses AllClauses Bindings) 
  _ _ _ _ -> false) 

(define answer
    [ ] _ -> true
    Variables Bindings -> (show-answer Variables Bindings))

(define show-answer
   [ ] _ -> (not (y-or-n? "~%more? "))
   [Variable | Variables] Bindings 
   -> (do (output "~%~A = ~A" Variable (dereference Variable Bindings))
              (show-answer Variables Bindings)))